<?php
#
# dmBridge: a data access framework for CONTENTdm(R)
#
# Copyright © 2009, 2010, 2011 Board of Regents of the Nevada System of Higher
# Education, on behalf of the University of Nevada, Las Vegas
#

/**
 * Maps an incoming HTTP request to actionable information (controller,
 * method, method parameters, etc.) in the Front Controller pattern. An
 * instance of the incoming route is accessible via
 * <code>DMHTTPRequest::getCurrent()->getURI()->getRoute()</code>.
 *
 * @author Alex Dolski <alex.dolski@unlv.edu>
 * @license http://www.opensource.org/licenses/mit-license.php
 */
final class DMRoute {

	/**
	 * @var array Array of available representations, as MIME type strings
	 */
	private $available_representations = array();

	/**
	 * @var string Controller class name
	 */
	private $controller;

	/**
	 * @var boolean
	 */
	private $is_module_route = false;

	/**
	 * @var string Controller method name
	 */
	private $method;

 	/**
	 * @var string Comma-delimited list of parameters to pass into the
	 * controller method invocation
	 */
	private $parameters;


	/**
	 * @param string controller The controller that the action should
	 * instantiate
	 * @param string method The method that the action should invoke
	 * @throws DMInvalidRouteException if the specified action's controller or
	 * method are invalid (the controller or method does not exist).
	 */
	public function __construct($controller, $method) {
		$this->setController($controller);
		$this->setMethod($method);
	}

	/**
	 * @return array Associative array of URI extension => DMMediaType pairs.
	 */
	public function getAvailableRepresentations() {
		if (count($this->available_representations)) {
			return $this->available_representations;
		}
		return array(
			"json" => new DMMediaType("application", "json"),
			"xml" => new DMMediaType("application", "xml")
		);
	}

	/**
	 * @param array representations Array of DMMediaTypes
	 */
	public function setAvailableRepresentations(array $representations) {
		$this->available_representations = $representations;
	}

	/**
	 * @param string requested_representation Extension of requested
	 * representation (e.g. "xml" for XML, "json" for JSON, etc.)
	 * @return string
	 */
	public function getBestRepresentation($requested_representation = null) {
		$representation = null;

		// try to honor any explicit requests from URL extensions
		if ($requested_representation) {
			foreach ($this->getAvailableRepresentations() as $ext => $mime) {
				if ($requested_representation == $ext) {
					$representation = $ext;
					break;
				}
			}
		} else {
			// try to honor the best match from the Accept header
			$accept = content_negotiation::mime_all_negotiation();
			if (is_array($accept)) {
				foreach ($accept['type'] as $amime) {
					foreach ($this->getAvailableRepresentations() as $key => $fmime) {
						if ((string) $amime == (string) $fmime) { // supported
							$representation = $key;
							break(2);
						}
					}
				}
			} else {
				$reps = $this->getAvailableRepresentations();
				if (count($reps)) {
					$representation = key($reps);
				}
			}
		}
		return $representation ? $representation : "html";
	}

	/**
	 * @return string The name of the controller used by this route
	 */
	public function getController() {
		return $this->controller;
	}

	/**
	 * @param string name
	 */
	public function setController($name) {
		$this->controller = $name;
	}

	/**
	 * @param object object The object to compare
	 * @return boolean
	 */
	public function equals($object) {
		if (!$object instanceof DMRoute) {
			return false;
		}
		if ($this->getController() == $object->getController()
				&& $this->getMethod() == $object->getMethod()
				&& $this->getParameters() == $object->getParameters()) {
			return true;
		}
		return false;
	}

	/**
	 * @return string The name of the controller method to invoke
	 */
	public function getMethod() {
		return $this->method;
	}

	/**
	 * @param method The name of the controller method to invoke
	 */
	public function setMethod($method) {
		$this->method = DMString::underscoreToCamelCase($method);
	}

	/**
	 * @return boolean
	 */
	public function isModuleRoute() {
		return $this->is_module_route;
	}

	/**
	 * @param boolean bool
	 */
	public function setModuleRoute($bool) {
		$this->is_module_route = (bool) $bool;
	}

	/**
	 * @return Comma-delimited string
	 */
	public function getParameters() {
		return $this->parameters;
	}

	/**
	 * @param params Comma-delimited string
	 */
	public function setParameters($params) {
		$this->parameters = $params;
	}

	/**
	 * @return boolean True if the route can be mapped to a controller method;
	 * false if not.
	 */
	public function isValid() {
		return method_exists($this->getController(), $this->getMethod());
	}

}
